/**
 * Created with gokuai network technology Co.Ltd.
 * User: sundq
 * Date: 13-8-22
 * Time: 下午9:22
 */

var http = require('http');
var Path = require('path');
var fs = require('fs');
var url = require('url')
var crcClass = require('./lib/crc')
var querystring = require('querystring');
var libUtil = require('./lib/util');
var oauthUrl = 'o.gokuai.com';
var apiUrl = 'a.gokuai.com';
var restUrl = 'r.gokuai.com';
var machine = 'webdav';
var clientId = '1047ed884922dc4651b39840c548a680';
var clientSecret = '1ff2b33c837032d333dd6a870689a87d';
var request = require("request");
var querystring = require("querystring");
var async = require('async');
require('./lib/date-format');


var requestToken = function(cb){
    var self = this;
    var data = {
        'grant_type' : 'password',
        'username' : self.username,
        'password' : libUtil.md5(self.password),
        'client_id' : clientId,
        'client_secret' : clientSecret
    };
    var args = {
        "method": "POST",
        "url": "http://"+oauthUrl+"/oauth/token",
        'headers':{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'},
        "body": querystring.stringify(data)
    }

    return request(args, function(err, res, body){
        var code = res.statusCode;
        var data = JSON.parse(body);
        if(err){
            cb(err);
        }else{
            if(code == 200){
                self.token = data['access_token'];
                self.expires_in = parseInt(data['expires_in']);
                getUserInfo(self.token,function(err,data){
                    self.mount_id = data;
                    cb(null);
                });
            }else{
                cb(code);
            }
        }
    });
}

var getUserInfo = function(token,cb){
    var self = this;
    var data = {'token' : token};
    var args = {
        "method": "POST",
        "url": "http://"+apiUrl+"/get_myinfo",
        'headers':{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'},
        "body": querystring.stringify(data)
    }
    return request(args, function(err, res, body){
        var code = res.statusCode;
        var data = JSON.parse(body);
        if(err){
            cb(err);
        }else{
            if(code == 200){
                self.mount_id = data['mount_id'];
                cb(null,self.mount_id);
            }else{
                cb(code);
            }
        }
    });
}

var put_file = function(token, mount_id, fullpath,filepath,filename,data,callback) {
    var method = 'PUT';
    var date = libUtil.dateRFC822();
    var mount = mount_id;
    var path_hash = '';
    var server_addr = '';
    var upload_session = '';
    var blocksize = 1024*1024;
    var range_begin = 0;
    var range_end = range_begin + blocksize-1;//1024*1024;
    var filehash = '';
    var filesize = 0;
    var request_uri = libUtil.encodeRequestUri(fullpath);
    var sign = libUtil.generateSignature(method, date, mount, request_uri,clientSecret);

    //upload a file,do as follows:
    /*
     1.gk_add_new_file: get the pathhash and server address
     2.gk_upload_init:init the upload processs
     3.gk_upload_req:get the chunk need to be uoload
     4.gk_upload_part:upload the chunk data
     5.gk_upload_finish:end the upload process
     */

    var gk_add_new_file = function(){
        var head_params = {
            "x-gk-mount" : mount,
            "x-gk-filehash" : filehash,
            "x-gk-filesize" : filesize,
            "x-gk-machine" : machine,
            'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
            "Date" : date,
            "Authorization" : token + ':' + sign
        };

        var args = {
            "method": method,
            "url": "http://"+restUrl+request_uri,
            'headers':head_params
        }

        request(args, function(err, res, body){
            if(!err){
                var data = JSON.parse(body);
                if(res.statusCode == 200){
                    if(data['state'] == 0){
                        path_hash = data['hash'];
                        server_addr = data['server'];
                        gk_upload_init();
                    }else{
                        //don't need upload the file have exist in the server
                        callback(null);
                    }
                }else{
                    console.log(data['error_msg'].toString());
                    callback(data['error_msg'].toString());
                }

            }else{
                callback(err);
            }
        });
    }

    var gk_upload_init = function(){
        var hostname = url.parse(server_addr).hostname
        var head_params = {
            "x-gk-upload-filename" : libUtil.encodeRequestUri(filename),
            "x-gk-upload-pathhash" : path_hash, //how get the hash
            "x-gk-upload-filehash" : filehash,
            "x-gk-upload-filesize" : filesize,
            "x-gk-token" : token,
            'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
            "Date" : date,
        };
        var args = {
            "method": "POST",
            "url": "http://"+hostname+"/upload_init",
            'headers':head_params
        };
        request(args, function(err, res, data){
            if(!err){
                var body = JSON.parse(data);
                console.log('upload_init body:'+body +' '+res.statusCode);
                if(res.statusCode == 200){
                    upload_session = body['session'];
                    gk_upload_req();
                }else if(res.statusCode == 202){
                    callback(null);
                }else{
                    console.log('upload_init ' + body['error_msg'].toString());
                }
            }else{
                callback(err);
            }
        });
    }

    var gk_upload_req = function(){
        var hostname = url.parse(server_addr).hostname;
        var head_params = {
            "x-gk-upload-session" : upload_session,
            'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
        };

        var args = {
            "method": "GET",
            "url": "http://"+hostname+"/upload_req",
            'headers':head_params
        };

        request(args, function(err, res, data){
            if(!err){
                console.log('upload_req body:'+res.statusCode+' '+data);
                if(res.statusCode == 200){ //ok
                    var file_have_upload_size = parseInt(data);
                    if(file_have_upload_size >= filesize){//the file upload have complete
                        gk_upload_finish();
                        callback(null);
                    }else{
                        range_begin = file_have_upload_size;
                        range_end = (range_begin + blocksize -1) >= filesize?filesize-1:(range_begin+blocksize-1);
                        (range_begin < range_end) && gk_upload_part(filepath);
                    }

                }else if(res.statusCode == 401){
                    var body = JSON.parse(res.statusCode);
                    callback(body['error_msg'].toString());
                }else if(res.statusCode >= 500){ //server error
                    console.log('server side error');
                    callback(res.statusCode);
                };
            }else{
                callback(err);
            }
        });
    }

    var gk_upload_part = function(filepath){
        var sendData = '';
        console.log('current begin=%d, end=%d',range_begin,range_end);
        var fReadStream = fs.createReadStream(filepath, {
            bufferSize: blocksize,
            start: range_begin,
            end: range_end,
            encoding:'binary'
        });
        fReadStream.on('data', function (chunk) {
            sendData += chunk;
        });

        fReadStream.on('error', function (err) {
            console.log("read file error :"+err);
        });

        fReadStream.on('end', function () {
            var hostname = url.parse(server_addr).hostname;
            var head_params = {
                "x-gk-upload-range" : range_begin+"-"+range_end,
                "x-gk-upload-crc" : (crcClass.crc32(sendData) >>> 0),
                "x-gk-upload-session" : upload_session,
                'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8',
                'Content-Length':sendData ? sendData.length : 0
            };
            var options = {
                host: hostname,
                method: "PUT",
                path: '/upload_part'
            };

            options.headers = head_params;
            var resData = '';
            var req = http.request(options, function(res) {
                res.setEncoding('utf8');
                res.on('data', function (chunk) {
                    resData += chunk;
                });
                res.on('end', function() {
                    if(res.statusCode == 200){ //ok
                        range_begin = range_end + 1;
                        range_end = (range_begin + blocksize - 1) >= filesize?filesize-1:(range_begin+blocksize-1);
                        (range_begin < range_end) ? gk_upload_part(filepath):gk_upload_finish();
                    }else if(res.statusCode == 202){
                        gk_upload_finish();
                        callback(null);
                    }else if(res.statusCode == 409){
                        var body = JSON.parse(data);
                        range_begin = parseInt(body['expect']);
                        range_end = (range_begin + blocksize - 1) >= filesize?filesize-1:(range_begin+blocksize-1);
                        (range_begin < range_end) && gk_upload_part(filepath);
                    }else{
                        callback('some error');
                    }
                });
            });

            req.on('error', function(e) {
                callback(e);
            });

            sendData && req.write(sendData,'binary');
            req.end();
        });
    }


    var gk_upload_finish = function(){
        var hostname = url.parse(server_addr).hostname;
        var head_params = {
            "x-gk-upload-filesize" : filesize,
            "x-gk-upload-session" : upload_session
        };
        var args = {
            "method": "GET",
            "url": "http://"+hostname+"/upload_finish",
            'headers':head_params
        };

        request(args, function(err, res, data){
            if(!err){
                if(res.statusCode == 200){
                    callback(null);
                }else if(res.statusCode == 409){//there are some chunk need upload
                    gk_upload_req();//get the chunk which need to upload
                }else if(res.statusCode >= 500){ //server error
                    console.log('server side error');
                    callback(res.statusCode);
                }else{
                    callback(res.statusCode);
                }
            }else{
                callback(err);
            }
        });
        console.log('gk_upload_finish');
    }

    if(filepath){
        var exists = fs.existsSync(filepath);
        if(exists){
            var stats = fs.lstatSync(filepath);
            filesize = stats.size;
            libUtil.filesha1(filepath,function(err,hash){
                if(!err){
                    filehash = hash;
                    gk_add_new_file();
                }
            });
        }else{
            callback(filename +' don\'t exist');
            return;
        }
    }
}

exports.app = function(config){
    var self = this;
    self.username = config.username;
    self.password = config.password;
    self.put_queue = async.queue(function(task,callbck){
        put_file(task.token,task.mount_id,task.uri,task.path,task.filename,null,callbck);
    },1);

    return {
        requesttoken: function(cb){
            var cur_self = this;
            var data = {
                'grant_type' : 'password',
                'username' : self.username,
                'password' : libUtil.md5(self.password),
                'client_id' : clientId,
                'client_secret' : clientSecret
            };
            var args = {
                "method": "POST",
                "url": "http://"+oauthUrl+"/oauth/token",
                'headers':{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'},
                "body": querystring.stringify(data)
            }

            return request(args, function(err, res, body){
                var code = res.statusCode;
                var data = JSON.parse(body);
                if(err){
                    cb(err);
                }else{
                    if(code == 200){
                        self.token = data['access_token'];
                        self.expires_in = parseInt(data['expires_in']);
                        cur_self.getuserinfo(cb);
                    }else{
                        cb(code);
                    }
                }
            });
        },

        getuserinfo: function(cb){
            var data = {'token' : self.token};
            var args = {
                "method": "POST",
                "url": "http://"+apiUrl+"/get_myinfo",
                'headers':{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'},
                "body": querystring.stringify(data)
            }
            return request(args, function(err, res, body){
                var code = res.statusCode;
                var data = JSON.parse(body);
                if(err){
                    cb(err);
                }else{
                    if(code == 200){
                        self.mount_id = data['mount_id'];
                        cb(null);
                    }else{
                        cb(code);
                    }
                }
            });
        },

        getUserName:function(){
            return self.username;
        },

        getUserToken:function(){
            return self.token;
        },

        getUserMount_id:function(){
            return self.mount_id;
        },

        login:function(cb){
            if(!self.mount_id || !self.token){
                requestToken.call(self,function(err){
                    if(!err){
                        setInterval(requestToken.bind(self,function(){}),(self.expires_in - 5)*1000);//refresh token
                    }
                    cb(err);
                });
            }else{
                cb(null);
            }
        },

        // creates client object
        command: function(options){
            var options = options;
            return {
                //download file stream async //test ok
                createReadStream: function(start,end,path, cb){
                    this.metadata(path,function(err,metadata){
                        if(!err){
                            var headers = {};
                            if (typeof start == "number" && typeof end == "number"){
                                headers['Range'] = 'bytes=' +start + '-' + end
                            }
                            var args = {
                                "method": "GET",
                                "url": metadata['uri'],
                                'headers':headers
                            }
                            var stream = request(args,function(err,res,body){});
                            if(stream){
                                cb(null,stream);
                            }else{
                                cb('some thing error');
                            }
                        }else{
                            cb(err);
                        }
                    });
                },

                put: function(path,filename,cb){
                    var task_option = {
                        token:self.token,
                        mount_id:self.mount_id,
                        uri:path,
                        path:filename,
                        filename:Path.basename(filename)
                    };
                    self.put_queue.push(task_option,cb);
                },

                metadata: function get_metadata(fullpath, cb,next_try){
                    var method = "GET";
                    var date = libUtil.dateRFC822();
                    //var request_uri = libUtil.encodeRequestUri(fullpath)+"?net=in";//use in future
                    var request_uri = libUtil.encodeRequestUri(fullpath);
                    var sign = libUtil.generateSignature(method, date, self.mount_id, request_uri,clientSecret);

                    var head_params = {
                        "x-gk-mount" : self.mount_id,
                        "Date" : date,
                        "Content-Type":'application/x-www-form-urlencoded; charset=UTF-8',
                        "Authorization" : self.token + ':' + sign
                    };

                    var args = {
                        "method": method,
                        "url": "http://"+restUrl+request_uri,
                        "headers":head_params,
                    }
                    return request(args, function(err,res,body){
                        var code = res.statusCode;
                        if(!err){
                            if(4 == parseInt(code/100) && !next_try){//4xx
                                get_metadata.call(self,fullpath+'/',cb,true);
                            }else{
                                var error = err?err:((code !== 200)?code:null);
                                var metadata = error?{}:JSON.parse(body);
                                cb(error,metadata);
                            }
                        }else{
                            cb(err,null);
                        }

                    });
                },

                //
                // Loads a dropbox folder
                // (recursive by default)
                //
                readdir: function (fullpath, callback) {
                    var method = "LIST";
                    var date = libUtil.dateRFC822();
                    var request_uri = libUtil.encodeRequestUri(fullpath);
                    var sign = libUtil.generateSignature(method, date, self.mount_id, request_uri,clientSecret);

                    var head_params = {
                        "x-gk-mount" : self.mount_id,
                        "x-gk-dir" : 0,
                        "x-gk-start" : 0,
                        "x-gk-size" : 100,
                        "x-gk-order" : 'filename asc',
                        "Date" : date,
                        "Content-Type":'application/x-www-form-urlencoded; charset=UTF-8',
                        "Authorization" : self.token + ':' + sign
                    };

                    var args = {
                        "method": method,
                        "url": "http://"+restUrl+request_uri,
                        'headers':head_params,
                    }
                    return request(args, function(err,res,body){
                        var code = res.statusCode;
                        var error = err?err:((code !== 200)?code:null);
                        var data = error?[]:JSON.parse(body).list;
                        var files = {};
                        for(var key in data){
                            files[data[key]['filename']] = data[key];
                        }
                        callback(error,files);
                    });
                },

                //get the all the version of the file.
                version:function(path,callback){
                    var method = "VERSION";
                    var date = libUtil.dateRFC822();
                    var request_uri = libUtil.encodeRequestUri(path);
                    var sign = libUtil.generateSignature(method, date, self.mount_id, request_uri,clientSecret);

                    var head_params = {
                        "x-gk-mount" : self.mount_id,
                        "Date" : date,
                        "Authorization" : self.token + ':' + sign
                    };

                    var args = {
                        "method": method,
                        "url": "http://r.gokuai.com"+request_uri,
                        'headers':head_params,
                    }

                    return request(args, function(err,res,body){
                        var code = res.statusCode;
                        var error = err?err:((code !== 200)?code:null);
                        var versions = error?[]:JSON.parse(body);
                        callback(error,versions);
                    });
                },

                //还原历史版本，版本号利用version方法获取.
                revert:function(path,verion,callback){
                    var method = "REVERT";
                    var date = libUtil.dateRFC822();
                    var fullpath = path+"?v="+verion;
                    var request_uri = libUtil.encodeRequestUri(fullpath);
                    var sign = libUtil.generateSignature(method, date, self.mount_id, request_uri,clientSecret);

                    var head_params = {
                        "x-gk-mount" : self.mount_id,
                        "Date" : date,
                        "Authorization" : self.token + ':' + sign
                    };

                    var args = {
                        "method": method,
                        "url": "http://r.gokuai.com"+request_uri,
                        'headers':head_params,
                    }

                    return request(args, function(err,res,body){
                        var code = res.statusCode;
                        var error = err?err:((code !== 200)?code:null);
                        callback(error);
                    });
                },

                //搜索文件，scope是搜索范围，搜索范围0:全部、1：文件名、2：内容.
                search:function(keywords,scope,callback){
                    var method = "SEARCH";
                    var date = libUtil.dateRFC822();
                    var request_uri = libUtil.encodeRequestUri("");
                    var sign = libUtil.generateSignature(method, date, self.mount_id, request_uri,clientSecret);

                    var head_params = {
                        "x-gk-mount" : self.mount_id,
                        "x-gk-filename":keywords,
                        'x-gk-bool':scope,
                        "Date" : date,
                        "Authorization" : self.token + ':' + sign
                    };

                    var args = {
                        "method": method,
                        "url": "http://r.gokuai.com",
                        'headers':head_params,
                    }

                    return request(args, function(err,res,body){
                        var code = res.statusCode;
                        var result = error?[]:JSON.parse(body).list;
                        var error = err?err:((code !== 200)?code:null);
                        callback(error,result);
                    });
                },

                thumbnails: function(path, args, cb){
                },

                cp: function(from_path, to_path, cb){
                    var method = "COPY";
                    var date = libUtil.dateRFC822();
                    var request_uri = libUtil.encodeRequestUri(from_path);
                    var sign = libUtil.generateSignature(method, date, self.mount_id, request_uri,clientSecret);

                    var head_params = {
                        "x-gk-mount" : self.mount_id,
                        "x-gk-target-path" : libUtil.encodeRequestUri(to_path),
                        "x-gk-machine" : 'webdav',
                        "Date" : date,
                        "Content-Type":'application/x-www-form-urlencoded; charset=UTF-8',
                        "Authorization" : self.token + ':' + sign
                    };

                    var args = {
                        "method": method,
                        "url": "http://"+restUrl+request_uri,
                        'headers':head_params,
                    }
                    return request(args, function(err,res,body){
                        var code = res.statusCode;
                        var error = err?err:((code !== 200)?code:null);
                        cb(error);
                    });
                },

                mv: function(from_path, to_path, cb){
                    var method = "MOVE";
                    var date = libUtil.dateRFC822();
                    var request_uri = libUtil.encodeRequestUri(from_path);
                    var sign = libUtil.generateSignature(method, date, self.mount_id, request_uri,clientSecret);

                    var head_params = {
                        "x-gk-mount" : self.mount_id,
                        "x-gk-target-path" : libUtil.encodeRequestUri(to_path),
                        "x-gk-machine" : 'webdav',
                        "Date" : date,
                        "Content-Type":'application/x-www-form-urlencoded; charset=UTF-8',
                        "Authorization" : self.token + ':' + sign
                    };

                    var args = {
                        "method": method,
                        "url": "http://"+restUrl+request_uri,
                        'headers':head_params,
                    }
                    return request(args, function(err,res,body){
                        var code = res.statusCode;
                        var error = err?err:((code !== 200)?code:null);
                        cb(error);
                    });
                },

                rename: function(from_path, to_path, cb){
                    var method = "RENAME";
                    var date = libUtil.dateRFC822();
                    var request_uri = libUtil.encodeRequestUri(from_path);
                    var sign = libUtil.generateSignature(method, date, self.mount_id, request_uri,clientSecret);

                    var head_params = {
                        "x-gk-mount" : self.mount_id,
                        "x-gk-newname" : encodeURIComponent(to_path),
                        "x-gk-machine" : 'webdav',
                        "Date" : date,
                        "Content-Type":'application/x-www-form-urlencoded; charset=UTF-8',
                        "Authorization" : self.token + ':' + sign
                    };

                    var args = {
                        "method": method,
                        "url": "http://"+restUrl+request_uri,
                        'headers':head_params,
                    }
                    return request(args, function(err,res,body){
                        var code = res.statusCode;
                        var error = err?err:((code !== 200)?code:null);
                        cb(error);
                    });
                },

                rm: function(fullpath, cb){
                    var method = "DELETE";
                    var date = libUtil.dateRFC822();
                    var request_uri = libUtil.encodeRequestUri(fullpath);
                    var sign = libUtil.generateSignature(method, date, self.mount_id, request_uri,clientSecret);

                    var head_params = {
                        "x-gk-mount" : self.mount_id,
                        "x-gk-machine" : 'webdav',
                        "Date" : date,
                        "Content-Type":'application/x-www-form-urlencoded; charset=UTF-8',
                        "Authorization" : self.token + ':' + sign
                    };

                    var args = {
                        "method": method,
                        "url": "http://"+restUrl+request_uri,
                        'headers':head_params,
                    }
                    return request(args, function(err,res,body){
                        var code = res.statusCode;
                        var error = err?err:((code !== 200)?code:null);
                        cb(error);
                    });
                },

                mkdir: function (fullpath, callback) {
                    var method = "PUT";
                    var date = libUtil.dateRFC822();
                    var request_uri = libUtil.encodeRequestUri(fullpath);
                    var sign = libUtil.generateSignature(method, date, self.mount_id, request_uri,clientSecret);

                    var head_params = {
                        "x-gk-mount" : self.mount_id,
                        "x-gk-filesize" : 0,
                        "x-gk-machine" : 'webdav',
                        "Date" : date,
                        "Content-Type":'application/x-www-form-urlencoded; charset=UTF-8',
                        "Authorization" : self.token + ':' + sign
                    };

                    var args = {
                        "method": method,
                        "url": "http://"+restUrl+request_uri,
                        'headers':head_params,
                    }
                    return request(args, function(err,res,body){
                        var code = res.statusCode;
                        var error = err?err:((code !== 200)?code:null);
                        callback(error);
                    });
                }
            }
        }
    }

}
